<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>cannon.js - stacks demo</title>
    <link rel="stylesheet" href="css/style.css" type="text/css" />
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
  </head>
  <body>
    <script type="module">
      import * as CANNON from '../dist/cannon-es.js'
      import { Demo } from './js/Demo.js'

      /**
       * For debugging different kinds of stacks
       */

      const demo = new Demo()

      const size = 2
      const mass = 5

      function createTetra() {
        const vertices = [
          new CANNON.Vec3(0, 0, 0),
          new CANNON.Vec3(2, 0, 0),
          new CANNON.Vec3(0, 2, 0),
          new CANNON.Vec3(0, 0, 2),
        ]
        const offset = -0.35
        for (let i = 0; i < vertices.length; i++) {
          const v = vertices[i]
          v.x += offset
          v.y += offset
          v.z += offset
        }
        return new CANNON.ConvexPolyhedron({
          vertices: vertices,
          faces: [
            [0, 3, 2], // -x
            [0, 1, 3], // -y
            [0, 2, 1], // -z
            [1, 2, 3], // +xyz
          ],
        })
      }

      function createBoxPolyhedron(size = 1) {
        const box = new CANNON.Box(new CANNON.Vec3(size, size, size))
        return box.convexPolyhedronRepresentation
      }

      function createCompound(mass = 1) {
        const compoundBody = new CANNON.Body({ mass })
        const shape = new CANNON.Box(new CANNON.Vec3(0.5 * size, 0.5 * size, 0.5 * size))
        compoundBody.addShape(shape, new CANNON.Vec3(0, size, 0))
        compoundBody.addShape(shape, new CANNON.Vec3(0, 0, 0))
        compoundBody.addShape(shape, new CANNON.Vec3(0, -size, 0))
        compoundBody.addShape(shape, new CANNON.Vec3(size, -size, 0))
        return compoundBody
      }

      demo.addScene('sphere/sphere', () => {
        const world = setupWorld(demo)

        // Sphere 1
        const sphereShape = new CANNON.Sphere(size)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(sphereShape)
        body1.position.set(0, size * 3, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Sphere 2
        const body2 = new CANNON.Body({ mass })
        body2.addShape(sphereShape)
        body2.position.set(0, size, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('sphere/plane', () => {
        const world = setupWorld(demo)

        // Sphere
        const sphereShape = new CANNON.Sphere(size)
        const body = new CANNON.Body({ mass })
        body.addShape(sphereShape)
        body.position.set(0, size * 3, 0)
        world.addBody(body)
        demo.addVisual(body)
      })

      demo.addScene('sphere/box', () => {
        const world = setupWorld(demo)

        // Box
        const boxShape = new CANNON.Box(new CANNON.Vec3(size, size, size))
        const body1 = new CANNON.Body({ mass })
        body1.addShape(boxShape)
        body1.position.set(0, size, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Sphere
        const sphereShape = new CANNON.Sphere(size)
        const body2 = new CANNON.Body({ mass })
        body2.addShape(sphereShape)
        body2.position.set(0, size * 3, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('sphere/compound', () => {
        const world = setupWorld(demo)

        // Sphere
        const sphereShape = new CANNON.Sphere(size * 0.5)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(sphereShape)
        body1.position.set(size, size * 6, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Compound
        const body2 = createCompound(mass)
        body2.position.set(0, size * 3, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('sphere/convex', () => {
        const world = setupWorld(demo)

        // Sphere
        const sphereShape = new CANNON.Sphere(size * 0.5)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(sphereShape)
        body1.position.set(0, size * 6, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Convex tetra
        const tetraShape = createTetra()
        const body2 = new CANNON.Body({ mass })
        body2.addShape(tetraShape)
        body2.position.set(0, size, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('sphere/cylinder', () => {
        const world = setupWorld(demo)

        // Sphere
        const sphereShape = new CANNON.Sphere(size * 0.5)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(sphereShape)
        body1.position.set(0, size * 6, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body2 = new CANNON.Body({ mass })
        body2.addShape(cylinderShape)
        body2.position.set(0, size * 3, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('sphere/particle', () => {
        const world = setupWorld(demo)

        // Sphere
        const sphereShape = new CANNON.Sphere(size * 0.5)
        const body = new CANNON.Body({ mass })
        body.addShape(sphereShape)
        body.position.set(0, size, 0)
        world.addBody(body)
        demo.addVisual(body)

        // Particle
        const particle = new CANNON.Body({ mass: 1 })
        particle.addShape(new CANNON.Particle())
        particle.position.set(-0.02, size * 3, 0)
        world.addBody(particle)
        demo.addVisual(particle)
      })

      demo.addScene('plane/box', () => {
        const world = setupWorld(demo)

        // Box
        const boxShape = new CANNON.Box(new CANNON.Vec3(size, size, size))
        const body = new CANNON.Body({ mass })
        body.addShape(boxShape)
        body.position.set(0, size, 0)
        world.addBody(body)
        demo.addVisual(body)
      })

      demo.addScene('plane/compound', () => {
        const world = setupWorld(demo)

        // Compound
        const body = createCompound(5)
        body.position.set(0, size * 4, 0)
        world.addBody(body)
        demo.addVisual(body)
      })

      demo.addScene('plane/convex', () => {
        const world = setupWorld(demo)

        // Convex tetra
        const tetraShape = createTetra()
        const body = new CANNON.Body({ mass })
        body.addShape(tetraShape)
        body.position.set(0, size, 0)
        world.addBody(body)
        demo.addVisual(body)
      })

      demo.addScene('plane/cylinder', () => {
        const world = setupWorld(demo)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body = new CANNON.Body({ mass })
        body.addShape(cylinderShape)
        body.position.set(0, size * 3, 0)
        world.addBody(body)
        demo.addVisual(body)
      })

      demo.addScene('plane/particle', () => {
        const world = setupWorld(demo)

        // Particle
        const particle = new CANNON.Body({ mass: 1 })
        particle.addShape(new CANNON.Particle())
        particle.position.set(-0.02, size * 3, 0)
        world.addBody(particle)
        demo.addVisual(particle)
      })

      demo.addScene('box/box', () => {
        const world = setupWorld(demo)

        // Box 1
        const boxShape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5))
        const body1 = new CANNON.Body({ mass })
        body1.addShape(boxShape)
        body1.position.set(0, size, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Box 2
        const body2 = new CANNON.Body({ mass })
        body2.addShape(boxShape)
        body2.position.set(-size * 0.5, size * 3, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('box/compound', () => {
        const world = setupWorld(demo)

        // Compound
        const body1 = createCompound(5)
        body1.position.set(-size * 0.5, size * 2, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Box
        const boxShape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5))
        const body2 = new CANNON.Body({ mass })
        body2.addShape(boxShape)
        body2.position.set(0, size * 7, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('box/convex', () => {
        const world = setupWorld(demo)

        // Box
        const boxShape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5))
        const body1 = new CANNON.Body({ mass })
        body1.addShape(boxShape)
        body1.position.set(0, size * 2, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Convex tetra
        const tetraShape = createTetra(size)
        const body2 = new CANNON.Body({ mass })
        body2.addShape(tetraShape)
        body2.position.set(0, size * 5, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('box/cylinder', () => {
        const world = setupWorld(demo)

        // Box
        const boxShape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5))
        const body1 = new CANNON.Body({ mass })
        body1.addShape(boxShape)
        body1.position.set(0, size * 5, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body2 = new CANNON.Body({ mass })
        body2.addShape(cylinderShape)
        body2.position.set(0, size * 2, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('box/particle', () => {
        const world = setupWorld(demo)

        // Box
        const boxShape = new CANNON.Box(new CANNON.Vec3(size * 0.5, size * 0.5, size * 0.5))
        const body = new CANNON.Body({ mass })
        body.addShape(boxShape)
        body.position.set(0, size, 0)
        world.addBody(body)
        demo.addVisual(body)

        // Particle
        const particle = new CANNON.Body({ mass: 1 })
        particle.addShape(new CANNON.Particle())
        particle.position.set(0, size * 3, 0)
        world.addBody(particle)
        demo.addVisual(particle)
      })

      demo.addScene('compound/compound', () => {
        const world = setupWorld(demo)

        // Compound
        const body1 = createCompound(5)
        body1.position.set(-size * 0.5, size * 6, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Compound
        const body2 = createCompound(5)
        body2.position.set(-size * 0.5, size * 2, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('compound/convex', () => {
        const world = setupWorld(demo)

        // Convex tetra
        const tetraShape = createTetra()
        const body1 = new CANNON.Body({ mass })
        body1.addShape(tetraShape)
        body1.position.set(0, size * 3, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Compound
        const body2 = createCompound(5)
        body2.position.set(0, size, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('compound/cylinder', () => {
        const world = setupWorld(demo)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(cylinderShape)
        body1.position.set(0, size * 5, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Compound
        const body2 = createCompound(5)
        body2.position.set(0, size, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('compound/particle', () => {
        const world = setupWorld(demo)

        // Compound
        const body1 = createCompound(5)
        body1.position.set(0, size * 4, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Particle
        const particle = new CANNON.Body({ mass: 1 })
        particle.addShape(new CANNON.Particle())
        particle.position.set(0, size * 7, 0)
        world.addBody(particle)
        demo.addVisual(particle)
      })

      demo.addScene('convex/convex', () => {
        const world = setupWorld(demo)

        // Convex tetra
        const tetraShape = createTetra()
        const body1 = new CANNON.Body({ mass })
        body1.addShape(tetraShape)
        body1.position.set(-0.1, size * 5, 0.1)
        world.addBody(body1)
        demo.addVisual(body1)

        // Convex box
        // const tetraShape = createTetra()
        const boxShape = createBoxPolyhedron(size)
        const body2 = new CANNON.Body({ mass })
        body2.addShape(boxShape)
        body2.position.set(0, size * 3, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('convex/cylinder', () => {
        const world = setupWorld(demo)

        // Convex tetra
        const tetraShape = createTetra()
        const body1 = new CANNON.Body({ mass })
        body1.addShape(tetraShape)
        body1.position.set(-0.1, size * 5, 0.1)
        world.addBody(body1)
        demo.addVisual(body1)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body2 = new CANNON.Body({ mass })
        body2.addShape(cylinderShape)
        body2.position.set(0, size * 2, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('convex/particle', () => {
        const world = setupWorld(demo)

        // Convex tetra
        // const boxShape = createBoxPolyhedron(size)
        const tetraShape = createTetra()
        const body1 = new CANNON.Body({ mass })
        body1.addShape(tetraShape)
        body1.position.set(0, size, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Particle
        const particle = new CANNON.Body({ mass: 1 })
        particle.addShape(new CANNON.Particle())
        particle.position.set(0, size * 3, 0)
        world.addBody(particle)
        demo.addVisual(particle)
      })

      demo.addScene('cylinder/cylinder', () => {
        const world = setupWorld(demo)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(cylinderShape)
        body1.position.set(0, size * 3, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Cylinder
        const body2 = new CANNON.Body({ mass })
        body2.addShape(cylinderShape)
        body2.position.set(0, size * 6, 0)
        body2.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
        world.addBody(body2)
        demo.addVisual(body2)
      })

      demo.addScene('cylinder/particle', () => {
        const world = setupWorld(demo)

        // Cylinder
        const cylinderShape = new CANNON.Cylinder(size, size, size * 2, 10)
        const body1 = new CANNON.Body({ mass })
        body1.addShape(cylinderShape)
        body1.position.set(0, size * 2, 0)
        world.addBody(body1)
        demo.addVisual(body1)

        // Particle
        const particle = new CANNON.Body({ mass: 1 })
        particle.addShape(new CANNON.Particle())
        particle.position.set(0, size * 4, 0)
        world.addBody(particle)
        demo.addVisual(particle)
      })

      demo.start()

      function setupWorld(demo) {
        const world = demo.getWorld()
        world.gravity.set(0, -10, 0)

        // Max solver iterations: Use more for better force propagation, but keep in mind that it's not very computationally cheap!
        world.solver.iterations = 20

        // Tweak contact properties.
        // Contact stiffness - use to make softer/harder contacts
        world.defaultContactMaterial.contactEquationStiffness = 1e7

        // Stabilization time in number of timesteps
        world.defaultContactMaterial.contactEquationRelaxation = 5

        // Static ground plane
        const groundShape = new CANNON.Plane()
        const groundBody = new CANNON.Body({ mass: 0 })
        groundBody.addShape(groundShape)
        groundBody.position.set(0, -1, 0)
        groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
        world.addBody(groundBody)
        demo.addVisual(groundBody)

        return world
      }
    </script>
  </body>
</html>
